<!-- <!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <title>知识树</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
      text {
        pointer-events: none;
      }
    </style>
  </head>

  <body>
    <div id="chart"></div>
    <script>
      const data = {
        name: "Java面向对象程序设计",
        children: [
          {
            name: "第 6 章 Java 输入输出流与文件操作",
            children: [
              {
                name: "6.1 输入输出流概述",
              },
              {
                name: "6.2 输入流",
              },
              {
                name: "6.3 输出流",
              },
              {
                name: "6.4 对象的序列化",
              },
              {
                name: "6.5 File类",
              },
              {
                name: "6.6 RandomAccessFile类",
              },
            ],
          },
          {
            name: "第 7 章 Java 的多线程机制",
            children: [
              {
                name: "7.1 线程的概念和 Java 的多线程机制",
              },
              {
                name: "7.2 线程的状态与生命周期",
              },
              {
                name: "7.3 线程的优先级及线程的调度",
              },
              {
                name: "7.4 实现多线程的两种方法",
              },
              {
                name: "7.5 线程的常用方法",
              },
              {
                name: "7.6 线程的同步",
              },
              {
                name: "7.7 线程的联合",
              },
            ],
          },
          {
            name: "第 8 章 Java 的数据库编程",
            children: [
              {
                name: "8.1 数据库基础",
              },
              {
                name: "8.2 JDBC 概述",
              },
              {
                name: "8.3 建立数据库连接",
              },
              {
                name: "8.4 查询操作",
              },
              {
                name: "8.5 更新、添加与删除操作",
              },
              {
                name: "8.6 预编译语句—PreparedStatement",
              },
              {
                name: "8.7 事务处理",
              },
              {
                name: "8.8 批处理操作",
              },
            ],
          },
          {
            name: "第 9 章 常用工具类与集合框架",
            children: [
              {
                name: "9.1 Object 类与 Class 类",
              },
              {
                name: "9.2 基本数据类型的封装类",
              },
              {
                name: "9.3 Math 与 Random 类",
              },
              {
                name: "9.4 System 类与 Runtime 类",
              },
              {
                name: "9.5 Date、Calendar、Timer 类",
              },
              {
                name: "9.6 集合框架与泛型",
              },
            ],
          },
          {
            name: "第10章 网络编程",
            children: [
              {
                name: "10.1 URL编程",
              },
              {
                name: "10.2 基于TCP协议的Socket编程",
              },
              {
                name: "10.3 ServerSocket 类有四个构造方法",
              },
              {
                name: "10.4 ServerSocket 类的常用方法",
              },
              {
                name: "10.5 Socket 类有五个构造方法",
              },
              {
                name: "10.6 InetAddress 类的方法",
              },
            ],
          },
        ],
      };
      const ldata = {
        name: "Java面向对象程序设计",
        children: [
          {
            name: "第 1 章 Java 概述",
             children: [
              {
                name: "1.1 Java 的起源和特点",
                children: [
              {
                name: "1.1.1 Java 的起源",
              },
              {
                name: "1.1.2 Java 语言的特点",
              },
            ],
              },
              {
                name: "1.2 Java 的开发环境",
                children: [
              {
                name: "1.2.1 Java 三种平台简介",
              },
              {
                name: "1.2.2 安装 Java SE 平台",
              },
              {
                name: "1.2.3 安装 Java 编程环境",
              },
            ],
              },
              {
                name: "1.3 Java 程序的开发步骤",
              },
              {
                name: "1.4 Java 程序的构成",
              },
            ],
          },
          {
            name: "第 2 章 Java 语言基础",
            children: [
              {
                name: "2.1 Java 语法基础",
              },
              {
                name: "2.2 运算符与表达式",
              },
              {
                name: "2.3 流程控制语句",
              },
              {
                name: "2.4 数组",
              },
              {
                name: "2.5 字符串",
              },
            ],
          },
          {
            name: "第 3 章 Java 面向对象编程",
            children: [
              {
                name: "3.1 面向对象软件开发概述",
              },
              {
                name: "3.2 类的定义与对象的创建",
              },
              {
                name: "3.3 Java 的继承与多态",
              },
              {
                name: "3.4 Java 的非访问控制符",
              },
              {
                name: "3.5 Java 的包与访问控制符",
              },
              {
                name: "3.6 Java 的接口",
              },
              {
                name: "3.7 Java 的异常处理机制",
              },
              {
                name: "3.8 内部类与匿名类",
              },
            ],
          },
          {
            name: "第 4 章 Java 图形用户界面程序设计",
            children: [
              {
                name: "4.1 概述",
              },
              {
                name: "4. 2 AWT 和 Swing 组件",
              },
              {
                name: "4.3 事件处理",
              },
              {
                name: "4.4 布局管理器",
              },
              {
                name: "4.5 对话框",
              },
              {
                name: "4.6 菜单",
              },
              {
                name: "4.7 Java 的图形绘制",
              },
            ],
          },
          {
            name: "第 5 章 Applet 小程序设计",
             children: [
              {
                name: "5.1 Applet 简介",
              },
              {
                name: "5.2 Applet 类的基本概念",
              },
              {
                name: "5.3 Applet 的生命周期",
              },
              {
                name: "5.4 Applet 的参数传递",
              },
            ],

            // children: [
            //   {
            //     name: "E222noch",
            //   },
            //   {
            //     name: "E222noch",
            //   },
            //   {
            //     name: "E222noch",
            //     children: [
            //       {
            //         name: "E222noch",
            //       },
            //       {
            //         name: "E222noch",
            //       },
            //       {
            //         name: "E222noch",
            //       },
            //     ],
            //   },
            // ],
          },
        ],
      };

      //右边
      const hierarchy = d3.hierarchy(data);
      const tree = d3
        .tree(data)
        .size([800, 500])//前面是上下大小，后面是左右宽度
        .separation(function (a, b) {
          return (a.parent == b.parent ? 1 : 2) / a.depth;
        });
      const treeData = tree(hierarchy);

      //左边
      const hierarchy1 = d3.hierarchy(ldata);
      const tree1 = d3
        .tree(hierarchy1)
        .size([800, 500])
        .separation(function (a, b) {
          return (a.parent == b.parent ? 1 : 2) / a.depth;
        });
      const treeData1 = tree1(hierarchy1);

      //页面设置
      const svg = d3
        .select("#chart")
        .append("svg")
        .attr("width", 1200)
        .attr("height", 800)
        .style("background", "#FFFFFF");


      const zoomCall = d3.zoom().on("zoom", function (e) {
        console.log(e.transform);
      });
      const zoomG = svg.append("g");
      let translateStr = "";
      let scaleStr = "";
      svg.call(
        d3.zoom().on("zoom", function (e) {
          const { sourceEvent, transform } = e;
          scaleStr = `scale(${transform.k})`;
          translateStr = `translate(${transform.x}px,${transform.y}px)`;
          zoomG.style("transform", `${translateStr} ${scaleStr}`);
        })
      );
      const rightG = zoomG.append("g").style("transform", "translateX(550px)");
      const leftG = zoomG.append("g");


      const lselection = leftG.selectAll("rect").data(treeData1.descendants());//左边
      //字体背景
      lselection
        .enter()
        .append("rect")
        // .attr("x", (d) => 550 - d.y)
        // .attr("y", (d) => d.x - 10)
        .attr("x", (d) => 550 - d.y -50)//水平左右
        .attr("y", (d) => d.x - 8)//垂直上下
        .attr("width", "220")
        .attr("height", 15)
        .attr("fill", "green");

        //字体位置
      lselection
        .enter()
        .append("text")
        .attr("x", (d) => 550 - d.y + 50)
        .attr("y", (d) => d.x + 6)
        .attr("text-anchor", "middle")
        .text((d) => d.data.name);

      const llinkSel = leftG.selectAll("path").data(treeData1.links());
      // const ge = d3.linkHorizontal().x(d => d.y).y(d => d.x);
      const ge1 = function (data) {
        
        const { source, target } = data;
        const point = [];
        point.push([source.x + 100, source.y]);
        point.push([source.x + 50, source.y]);
        point.push([source.x + 50, target.y]);
        point.push([target.x, target.y]);
        return "M" + point.join("L");
      };
      llinkSel
        .enter()
        .append("path")
        .attr("d", (d) => {
          const start = {
            y: d.source.x,
            x: 550 - (d.source.y + 100),
          };
          const end = {
            y: d.target.x,
            x: 550 - d.target.y + 100,
          };
          return ge1({
            source: start,
            target: end,
          });
        })
        .attr("fill", "none")
        .attr("stroke", "green")
        .attr("stroke-width", "1");

      const selection = rightG.selectAll("rect").data(treeData.descendants());//右边
      selection
        .enter()
        .append("rect")
        .attr("x", (d) => d.y-50)
        .attr("y", (d) => d.x - 8)
        .attr("width", "220")
        .attr("height", 15)
        .attr("fill", "green")
        .on("click", (e) => {
          console.log(e);
        });
      selection
        .enter()
        .append("text")
        .attr("x", (d) => d.y + 50)
        .attr("y", (d) => d.x + 6)
        .attr("text-anchor", "middle")
        .text((d) => d.data.name);

      const linkSel = rightG.selectAll("path").data(treeData.links());
      // const ge = d3.linkHorizontal().x(d => d.y).y(d => d.x);
      const ge = function (data) {
        const { source, target } = data;
        const point = [];
        point.push([source.x, source.y]);
        point.push([source.x + 50, source.y]);
        point.push([source.x + 50, target.y]);
        point.push([target.x, target.y]);
        return "M" + point.join("L");
      };
      linkSel
        .enter()
        .append("path")
        .attr("d", (d) => {
          const start = {
            y: d.source.x,
            x: d.source.y + 100,
          };
          const end = {
            y: d.target.x,
            x: d.target.y,
          };
          return ge({
            source: start,
            target: end,
          });
        })
        .attr("fill", "none")
        .attr("stroke", "green")
        .attr("stroke-width", "1");
    </script>
  </body>
</html>
 -->
